package pers.mihao.quickstream.common.plus.wrapper;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
import org.springframework.core.Ordered;
import pers.mihao.quickstream.common.util.spring.SpringUtil;

import java.util.*;

import static pers.mihao.quickstream.common.util.ObjectUtil.getInterface;

/**
 * @author mihao
 * @since 2022/8/1
 */
public class BeanWrapperCreatePostProcessor implements SmartInstantiationAwareBeanPostProcessor,
        BeanFactoryAware, Ordered {

    Logger log = LoggerFactory.getLogger(this.getClass());
    ConfigurableListableBeanFactory configurableListableBeanFactory;
    private BeanFactory beanFactory;
    Map<Class<?>, TreeSet<WrapperCreateInfo>> wrapperClassMap;
    private final Set<String> wrappedBeanName = new HashSet<>();

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return wrapperBean(bean, beanName);
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    }

    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
        return wrapperBean(bean, beanName);
    }

    private Object wrapperBean(Object bean, String beanName) {
        if (wrappedBeanName.contains(beanName)) {
            return bean;
        }
        List<Class<?>> list = null;
        for (Class<?> clazz : getInterface(bean.getClass())) {
            if (wrapperClassMap.containsKey(clazz)) {
                if (list == null) list = new ArrayList<>();
                list.add(clazz);
            }
        }

        if (list != null) {
            TreeSet<WrapperCreateInfo> wrapperSet = new TreeSet<>();
            for (Class<?> clazz : list) {
                wrapperSet.addAll(wrapperClassMap.get(clazz));
            }

            for (WrapperCreateInfo wrapperCreateInfo : wrapperSet) {
                if (wrapperCreateInfo.wrappedSet.contains(beanName)) {
                    continue;
                }
                wrapperCreateInfo.wrappedSet.add(beanName);
                try {
                    bean = SpringUtil.createBeanWrapper(beanFactory, wrapperCreateInfo.wrapperClass, beanName, bean);
                } catch (Exception e) {
                    log.error("wrapper bean error beanName:{} error {}", beanName, e.getMessage());
                }
                bean = beanWrapperPostProcess(bean, beanName);
            }
        }
        wrappedBeanName.add(beanName);
        return bean;
    }

    private Object beanWrapperPostProcess(Object bean, String beanName) {
        return bean;
    }


    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    public void setWrapperClassMap(Map<Class<?>, TreeSet<WrapperCreateInfo>> wrapperClassMap) {
        this.wrapperClassMap = wrapperClassMap;
    }
}
